/*
 *  LivingSpot.h
 *  
*/

#ifndef LivingSpot_h
#define LivingSpot_h

#include "WProgram.h"
#include "hardwareIO.h"
//#include "classMidi.h" // no need, because we only use methods through the extern object myMidi

// parametres a regler a la main:
//(1) good values a regler a la main:
// Example: values for SnuMoa (Seoul):
#define MAX_INTENSITE 200 //70
#define MIN_INTENSITE 100 //55
// parameters for autothreshold:
#define contrast_factor 1.5 // we have: MIN_CONTRAST_AD=(MAX_INTENSITE-MIN_INTENSITE)/contrast_factor
#define threshold_factor 0.6 // 0.6 is a good value
// for fixed threshold:
#define FIXED_THRESHOLD 100


//maximum value used by the DAC to controle the mirrors.
//The DAC is 12 bytes capable (Max=4096); but values under 4000 are "cut" by the mirrors
#define MAXRADIUS 1000

#define MAXSAMPLES 50 // this is TWICE the max number of points allowed by saccade (to avoid dynamic allocation of memory)

//#define GRAVITYCONST 0.01
//#define MASSE 30000

const static int tableSLPLogo_size = 53;
const static int tableSLPLogo_X[tableSLPLogo_size] = {7.0, 7.0, 6.0, 5.0, 4.0, 3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.0, 7.0, 7.0, 6.0, 5.0, 4.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 0.0, -1.0, -2.0, -2.0, -2.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0, -6.0, -7.0, -7.0, -7.0, -7.0, -6.0, -5.0, -4.0, -3.0};
const static int tableSLPLogo_Y[tableSLPLogo_size] = {-2.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 3.0, 3.0, 3.0, 3.0, 2.0, 3.0, 3.0, 2.0, 1.0, 0.0, -1.0, -2.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.0, -3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 3.0, 3.0, 3.0, 3.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0};

// behaviour mode (behaviourMode state variable):
#define SIMPLE_BOUNCING 0
#define FAST_BOUNCING 1
#define NEWTON_DYNAMICS 2
#define COUNTOUR_FOLLOWING 3
#define SNELL_LAW 4

// border mode (int borderMode):
#define NORMAL_REFLEXION 0
#define RETRO_REFLEXION 1
#define RESET_POSITION 2

class LivingSpot
{
  public:
  //public Variables
	
	// Behaviour mode:
	bool alive;
	int behaviourMode;// rem: some behaviors could be combined, but for the time being this will be avoided
	// rem: this variable will change the way the update method works.
	
	// Spot state relating to sound generation and special event detection (to put in another class in the future):
	// (rem: this somehow copies the CSpotInformation class in older system)
	int identifier; //0, 1, 2...
        char spotName[10]; //spot0, spot1 ...
        
	int maxI;// = 0.0; // note: maxI and minI are defined as public variables now (easy access as relevant spot information). 
	int minI;// = 1024.0; // the 8 bit readout (fast reading) gives values between 0 and 255, and in high res (10 bits), values are from 0 to 1023
	//float _x1, _x2;// note: position is to avoid manipulating mistakes (avoid out of range values), but we may need to get their value for sound generation
        // so we had getX() and getY(). If this is too time consuming, we can of course use public variables.  
	float depth; // this is the z coordinate: not used yet...
	float x1, y1; // this is the position at time t-dt (for verdet integration). The position at time t is (_x, _y) and are private variables.
        float placeX, placeY; // these are the coordinates of a special position (the initial coordinates, the center of attraction, etc).      
        boolean firstTime;
        boolean firstTimeStartRefraction;
        
        float initVx, initVy; // initial speed
        float vx, vy; //current speed (rem: use updateSpeed when newtonian dynamics because of the Verdet integration)
        
        // new for bounce mode: 
        boolean phoenixSpeedMode;// phoenix speed mode true means that when speed is too low, it will be regenerated by a touch 
        float phoenixNormSpeed; // norm of the speed the first time a collision occurs 
        float resetSpeedThreshold; // when speed norm goes down this threshold, the firstTimeCollision becomes true again. 
        float bounce_collisionDamping; // 1 means no damping when collision (in bounce mode)
        float bounce_movingDamping; // 1 means no damping as it moves (in bounce mode only)
       
	float ax, ay; // current acceleration
	float target_angle;// the direction of the recentering vector in radians
	// float radiusGyration; // not used for the time being: this can be time consuming in Arduino
	// int detectedCorner; // perhaps time consuming (uses radiusGyration)
	boolean touchWall; // detection of wall borders
	boolean collision;// detection of collision with other particules
        boolean onContour; // this is to indicate that the spot is on a contour, or has lost it. 
        int searchCounter;

	// parameters for NEWTONIAN DYNAMICS only:
	float totalForceX, totalForceY; // acceleration is calculated form this, divided by the mass of the particle
	// bouncing force:
	float bouncingForceFactor; // this is for computing the force produced by bouncing on CONTOURS (at least when in newton mode), 	
	float centralForceFactor; // to compute the force given by a point of central attraction 
	float gx, gy; // this is the direction and norm of the "parallel" acceleration 
	float interForceX, interForceY; // the force given by interaction with other particles or objects (gravity+electrostatic), calculated in the main program
	float interForceFactor; // rem: the factors for this force are given by the mass and charge AND this additional factor
	float visForceFactor; // for calculating the viscosity force
	
	float rangeInteractionLong, rangeInteractionShort; //interaction length for attractive/repulsive force ( must be calibrated!! - potentiometer? based on spot diameter?)

	float mass;   // for calculating the gravitational force, as well as the acceleration
	float charge; // for calculating electrostatic force
      
       // interaction modes on/off, to avoid setting the constants to 0 if one does not want certain forces to work
	
        // NEWTONIAN dynamics mode:
	boolean bouncingForceMode;// // this is bouncing by force generationtrue
        boolean bouncingSpecularMode;  // this is bouncing by inverting speed
	boolean centralForceMode;//=true; // default mode is CENTRAL force active
	boolean parallelForceMode;//=false; // default mode is parallel force inactive
	boolean particleInteractionMode; //= false;// default mode false
	boolean visForceMode;// =false; // default mode false
	
      // border mode:
       boolean borderDamping;
       int borderMode; // can be: NORMAL_REFLEXION;//RESET_POSITION;// RETRO_REFLEXION;
        boolean showLimits;
        boolean showGreenFlash;
        
	// Inter-particle collision detection (meaning that another particle is in nearby, inside collisionRadius)
	float collisionRadius; // must be calibrated!
      
        float whiteProportion;
        int pleaseContinue;

	float dt; // this is the time interval in microseconds!
        float slowTime; //(the effective dt will be dt*slowTime)
        unsigned long oldTime; // this is to count dt (must be initialized). 

	int xMax, yMax, xMin, yMin; //border of the mirrors (normaly, min=0, max=4000)
	float dx, dy;   //this is the "recentering vector", normal to the interface, and pointing towards the dark part, computed after performing a saccade (this is done in the computeNormal function). 
	float NormVector; //Norm of (dx, dy)r
	float nx, ny; //normalized value for (dx, dy)
	int autoThreshold; //threshold computed automaticaly to define dark and light area
	int fixedThreshold; //current threshod used to define dark and light area
	boolean thresholdingMode; //define the use of a fixed or an automatic threshold
	boolean binary_smoothing_Mode;
	float error_angle; //correction angle due to the delai on the lockIn amplifier
	float thresholdFactorFB; //threshold factor used by the fast bouncing
	float speedContourFollowing; //"speed" factor used on moveAlongContour (percentage of the current radius)
	
  //public Methods
    
        // Note: parameterless default constructor not overloaded
        
        //LivingSpot(int blinkTimes); // basically, for tests using a LED
        
	//LivingSpot(int threshold = -1, int posX = 2000.0, int posY = 2000.0, int saccadeRadius = 100, int numSample = 18, int id=0); //full parameters constructor
	
        void Init(int threshold, int posX, int posY, int saccadeRadius, int numSample, int id);

	// Special controls:
        void updateSpeed(float speedX, float speedY);
	int updatePosition(float posX, float posY); //update the position of the spot
	int moveSpot(float posX, float posY); //update the position of the spot and move it
	void drawMaxArea(int nloops = 2); //draw a squarre corresponding to the maximum area.
	
	// spot behaviours:
        void setPhoenixSpeed(float phoenixNormSpeed, float thresholdReinitSpeed);
	int lightRefraction();
	int newtonDynamics(int numSpots); 
	int fastBouncing(); //go straight until a dark edge, perform a saccade and bounce according to the reflection direction
	int scanBouncing(); //scan and bounce on dark edge
	int simpleBouncing(int numSpots); // simple bouncing mode, doing a saccade all the time
	bool moveAlongContour(int numSpots); // this function moves along the direction perpendicular to the contour (at the instant it is called) by a factor of the diameter
	bool searchContour(int numSpots);

        int computeInteraction(LivingSpot* spotArray, int numSpots); // used for particle interaction when in newtonian dynamics mode

	// the global update function that will call one of the spot behaviours above:
	void update(LivingSpot* spotArray, int numSpots);
	
	// other special behaviours:
	void drawSLPLogo(int centerLogo_X = 2000, int centerLogo_Y = 2000, int sizeLogo_X = 80, int sizeLogo_Y = 80, int nloops = 1); //draw logo
	void drawSLPLogoAngle(float angleLogo, int centerLogo_X = 2000, int centerLogo_Y = 2000, int sizeLogo_X = 80, int sizeLogo_Y = 80, int nloops = 1); //draw the logo oriented with a custom angle
	void drawSLPLogoRotating(int speedRotation = 2, int centerLogo_X = 2000, int centerLogo_Y = 2000, int sizeLogo_X = 80, int sizeLogo_Y = 80); //draw a rotation SLP Logo on a complete 360 deg loop
	
	float getX(); //return the value of the protected variable _x
	float getY(); //return the value of the protected variable _y

	//related to the circular saccade:
	int computeNormalVector(float x, float y, int radius); //compute the normal vector of a contour by performing a saccade
	void setNumSamples(int numSamples); //sets the number of samples, and update the saccade table accordingly
	int setRadiusSaccade(int radius);  // will change _saccadeRadius, and take care that the new radius does not exceed a certain ad-hoc value, nor is negative (in such case, the function return -1)
	
        void setAutoThreshold(boolean);
        void updateFixedThreshold(int threshold); //update the threshold used by the saccade (-1 change the mode to "auto threshold")
      
        void measureAngle();
	
  private:
	
	//NOTE: for some strange reason, the STATIC keyword does not work in a class compiled for Arduino??? This means that the number of spots should be inferred from the length of the spotArray, 
	// probably using a global variable in the main program... ugly.
	// static int numberOfSpots;//=-1; // this variable will be shared by ALL the instances of the class, and its initialization is done ONLY ONCE. 
	// its value will be increased each time we instantiate a new object, and decreased each time we delete one.
	
	float _x, _y; //current position
        ///float vx, vy; //current speed 
	
	//variable related to the circular saccade
	float _saccadeRadius; //radius of the circular saccade
	int _numSamples; //number of sampling spots (distributed uniformly around the saccade circle)
	

/*
        int *_saccadePosTab; //integer table containing the position of each sampling spots (created by 'void updateSaccadeTab()') (double size one dimension table for X and Y)
	int *_saccadeIntesityTab; //integer table containing the value of the lockIn Amplifier for each sampling spots (created by 'void updateSaccadeTab()')
	byte *_saccadeBinaryTab; //binary table of the saccade (0 = dark area; 1 = light area)
	byte *_transBinaryTab; //binary table used by erode() and dilate(), binary transition vector
*/
	 float _saccadePosTab[MAXSAMPLES];
         int _saccadeIntesityTab[MAXSAMPLES];
         byte _saccadeBinaryTab[MAXSAMPLES]; 
         byte _transBinaryTab[MAXSAMPLES];
         
//variable for fastBouncing
	const static int _nbAvr = 4;
	int _averageLockIn;
	int _avLockInTab[_nbAvr];
	int _idAv;	
	int _idSaccade;
		
  //private Methods
  
	int _saccade(float x, float y, int radius); //perform a scanning saccade and compute the binary data table of the saccade
	void _Dilate();
	void _Erode();
        float  refractionIndex(int intensity); // simulated refraction index from measured intensity
	
};

#endif

